/*
 * @Author: xiaosihan
 * @Date: 2024-10-25 08:13:05
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-11-01 00:44:28
 */

import {
  WebGLRenderer,
  Camera,
  Object3D,
  DepthTexture,
  HalfFloatType,
  WebGLRenderTarget,
  UniformsUtils,
  ShaderMaterial,
  AdditiveBlending,
  NoBlending,
  ShaderChunk,
  Vector2,
  Scene,
  DepthFormat,
  LinearFilter,
  RGBAFormat,
  UnsignedShortType,
} from 'three';
import CombineShader from './CombineShader';
import FullScreenQuad from 'three-base/pass/FullScreenQuad';
import ConvolutionShader from './ConvolutionShader';

// 选中对象的深度纹理
const selectDepthTexture = (() => {
  const baseDepthTexture = new DepthTexture(4096, 4096);
  baseDepthTexture.format = DepthFormat;
  baseDepthTexture.type = UnsignedShortType;
  baseDepthTexture.anisotropy = 16;
  return baseDepthTexture;
})();

// 选中对象的纹理
const selectTextureTarget = new WebGLRenderTarget(512, 512, {
  minFilter: LinearFilter,
  magFilter: LinearFilter,
  format: RGBAFormat,
  depthTexture: selectDepthTexture,
  anisotropy: 16,
});

// 深度测试后的选中纹理
const selectDethpTestTextureTarget = new WebGLRenderTarget(512, 512, {
  minFilter: LinearFilter,
  magFilter: LinearFilter,
  format: RGBAFormat,
  anisotropy: 16,
});

const blurX = new Vector2(0.001953125, 0.0);
const blurY = new Vector2(0.0, 0.001953125);

const renderTargetX = new WebGLRenderTarget(1, 1, { type: HalfFloatType }); // will be resized later
// const renderTargetX.texture.name = 'BloomPass.x';
const renderTargetY = new WebGLRenderTarget(1, 1, { type: HalfFloatType }); // will be resized later
// const renderTargetY.texture.name = 'BloomPass.y';

const combineUniforms = UniformsUtils.clone(CombineShader.uniforms);
combineUniforms['strength'].value = 1; // strength;
const materialCombine = new ShaderMaterial({
  name: CombineShader.name,
  uniforms: combineUniforms,
  vertexShader: CombineShader.vertexShader,
  fragmentShader: CombineShader.fragmentShader,
  blending: AdditiveBlending,
  transparent: true,
});

// 卷积着色器材质
const materialConvolution = new ShaderMaterial({
  name: ConvolutionShader.name,
  uniforms: {
    tDiffuse: { value: null },
    uImageIncrement: { value: new Vector2(0.001953125, 0.0) },
    cKernel: { value: ConvolutionShader.buildKernel(10) },
  },
  vertexShader: ConvolutionShader.vertexShader,
  fragmentShader: ConvolutionShader.fragmentShader,
  defines: {
    KERNEL_SIZE_FLOAT: '25.0',
    KERNEL_SIZE_INT: '25',
  },
});

const size = new Vector2();
// 场景对象
const scene = new Scene();

// 创建一个渲染工具 用于处理纹理
const fsQuad = new FullScreenQuad(); // 渲染辅助器

//深度比对纹理
const depthTestShader = new ShaderMaterial({
  uniforms: {
    map: { value: null }, // 贴图纹理
    baseDepth: { value: null }, // 整个场景的深度纹理
    selectDepth: { value: null }, // 选中的对象深度纹理
  },
  vertexShader: `
        ${ShaderChunk.common}
        ${ShaderChunk.logdepthbuf_pars_vertex}
        varying vec2 vUv;
        void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
            ${ShaderChunk.logdepthbuf_vertex}
        }
    `,
  fragmentShader: `
        ${ShaderChunk.logdepthbuf_pars_fragment}
        uniform sampler2D map;
        uniform sampler2D baseDepth;
        uniform sampler2D selectDepth;
        varying vec2 vUv;
        void main() {
            if(texture2D(baseDepth, vUv).r < texture2D(selectDepth, vUv).r){
                discard;
            }
            if(texture2D(baseDepth, vUv).r == 1.0){
                discard;
            }
            gl_FragColor = texture2D(map, vUv);
            ${ShaderChunk.logdepthbuf_fragment}
        }
    `,
  blending: NoBlending, // 渲染时不与背景融合计算 直接替换
  depthTest: true,
  depthWrite: true,
  transparent: true,
});

const copyShader = new ShaderMaterial({
  uniforms: {
    map: { value: null }, // 贴图纹理
  },
  vertexShader: `
        varying vec2 vUv;
        void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }
    `,
  fragmentShader: `
        uniform sampler2D map;
        varying vec2 vUv;
        void main() {
            gl_FragColor = texture2D(map, vUv);
        }
    `,
  depthTest: true,
  depthWrite: true,
  transparent: true,
});

// 发光通道
export default function bloom_pass(renderer: WebGLRenderer, camera: Camera, object3d: Object3D, baseRenderTarget: WebGLRenderTarget, baseDepthTexture: DepthTexture) {
  //   const { bloom_enable = false } = object3d.userData;
  //   if (!bloom_enable) {
  //     return;
  //   }

  // 保存原帧缓冲区
  const originRenderTarget = renderer.getRenderTarget();

  // 设置选中目标的帧缓冲区
  renderer.getSize(size);
  size.multiplyScalar(window.devicePixelRatio);
  selectTextureTarget.setSize(size.x, size.y);
  selectDethpTestTextureTarget.setSize(size.x, size.y);
  renderTargetX.setSize(size.x, size.y);
  renderTargetY.setSize(size.x, size.y);
  renderer.setRenderTarget(selectTextureTarget);
  scene.children = [object3d];
  renderer.clear();
  renderer.render(scene, camera);

  //深度测试提取目标的颜色纹理
  renderer.setRenderTarget(selectDethpTestTextureTarget);
  renderer.clear();
  depthTestShader.uniforms.map.value = baseRenderTarget.texture;
  depthTestShader.uniforms.baseDepth.value = baseDepthTexture;
  depthTestShader.uniforms.selectDepth.value = selectDepthTexture;
  fsQuad.render(renderer, depthTestShader);

  //bloom shader 处理
  materialConvolution.uniforms['tDiffuse'].value = selectDethpTestTextureTarget.texture;
  materialConvolution.uniforms['uImageIncrement'].value = blurX;
  renderer.setRenderTarget(renderTargetX);
  renderer.clear();
  fsQuad.render(renderer, materialConvolution);

  materialConvolution.uniforms['tDiffuse'].value = renderTargetX.texture;
  materialConvolution.uniforms['uImageIncrement'].value = blurY;
  renderer.setRenderTarget(renderTargetY);
  renderer.clear();
  fsQuad.render(renderer, materialConvolution);

  materialCombine.uniforms['tDiffuse'].value = renderTargetX.texture;
  renderer.setRenderTarget(null); //renderTargetY);
  renderer.clearDepth();
  fsQuad.render(renderer, materialCombine);

  // 调试代码
  // renderer.setRenderTarget(originRenderTarget);
  // renderer.clear();
  // copyShader.uniforms.map.value = blur2TextureTarget.texture;
  // fsQuad.render(renderer, copyShader);
  // return;
}
